package com.qf.fmall2202.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.fmall2202.exception.QfException;
import com.qf.fmall2202.user.dao.UsersDao;
import com.qf.fmall2202.user.entity.User;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.fmall2202.user.service.UsersService;
import com.qf.fmall2202.user.shiro.ShiroConstants;
import com.qf.fmall2202.utils.CommonConstants;
import com.qf.fmall2202.vo.ResultVo;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 * 用户  服务实现类
 * </p>
 *
 * @author 徐英海
 * @since 2022-11-03
 */
@Service
public class UsersServiceImpl extends ServiceImpl<UsersDao, User> implements UsersService {
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultVo regist(User user) {
        String username = user.getUsername();
        String password = user.getPassword();
        String repassword = user.getRepassword();
        if (this.checkUsernameExist(username) != null) {
            throw new QfException("用户名重复");
        }
        if (!password.equals(repassword)) {
            throw new QfException("两次密码输入不一致！");
        }
        String salt = UUID.randomUUID().toString().replaceAll("-", String.valueOf(new Random().nextInt(10))).substring(0, 32);
        Md5Hash md5Hash = new Md5Hash(password, salt, ShiroConstants.HASH_ITERATIONS);
        user.setPassword(md5Hash.toHex());
        user.setSalt(salt);
        user.setUserRegtime(new Date());
        user.setUserModtime(new Date());
        this.save(user);
        return ResultVo.ok();
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public ResultVo login(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(new UsernamePasswordToken(username, password));
        } catch (AuthenticationException e) {
            throw new QfException("用户名或密码错误");
        }
        User user = (User) subject.getPrincipal();
        String token = createToken(user);
        return ResultVo.ok(token, user);
    }

    /**
     * 创建token字符串
     * 对于token的需求：
     * 1.能够让服务器端校验token是否合法（是不是服务器端颁发的token）
     * 2.时效性（能够指定过期时机）
     * 3.扩展性，可以自定义存入token的信息
     *
     * @param user
     * @return
     */
    private String createToken(User user) {
        JwtBuilder builder = Jwts.builder();
        Map<String, Object> map = new HashMap<>();
        try {
            String userJson = new ObjectMapper().writeValueAsString(user);
            map.put(CommonConstants.JWT_CLAIMS_KEY, userJson);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        return builder
                .setClaims(map)
                .setSubject(user.getUsername())
                .setIssuedAt(new Date())
                .setIssuer(CommonConstants.JWT_AUTHOR)
                .setId(UUID.randomUUID().toString())
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
                .signWith(SignatureAlgorithm.HS256, CommonConstants.JWT_SECRET_KEY)
                .compact();
    }

    private User checkUsernameExist(String username) {
        QueryWrapper<User> usersQueryWrapper = new QueryWrapper<>();
        usersQueryWrapper.eq("username", username);
        return this.getOne(usersQueryWrapper);
    }
}
